private void read(String expected) {
//用`、[]、“包围起来的字符串所代表的Token会使得currentTokenQuoted=true
//如"CREATE or `REPLACE` TABLE IF NOT EXISTS
//expected = currentToken = REPLACE
//currentTokenQuoted = true
if (currentTokenQuoted || !equalsToken(expected, currentToken)) {
addExpected(expected);
throw getSyntaxError();
}
read();
}
//read与readIf的差别
//read: expected与currentToken必须一样,不一样则报语法错误,如是没有语法错误预读下一个token
//readIf: 只有token与currentToken一样时才预读下一个token,不相同不会报语法错误
private boolean readIf(String token) {
if (!currentTokenQuoted && equalsToken(token, currentToken)) {
read();
return true;
}
addExpected(token);
return false;
}
//与readIf(String token)大部份相同,唯一差别是isToken不会预读下一个token
private boolean isToken(String token) {
boolean result = equalsToken(token, currentToken) && !currentTokenQuoted;
if (result) {
return true;
}
addExpected(token);
return false;
}
private boolean equalsToken(String a, String b) {
if (a == null) {
return b == null;
} else if (a.equals(b)) {
return true;
} else if (!identifiersToUpper && a.equalsIgnoreCase(b)) {
return true;
}
return false;
}
private void read() {
currentTokenQuoted = false;
if (expectedList != null) {
expectedList.clear();
}
int[] types = characterTypes;
lastParseIndex = parseIndex;
int i = parseIndex;
int type = types[i];
while (type == 0) { //跳过最前面type为0的元素,因为0对应的字符是空白类的,没意义
type = types[++i];
}
int start = i;
char[] chars = sqlCommandChars;
char c = chars[i++];
currentToken = "";
switch (type) {
case CHAR_NAME:
while (true) {
type = types[i];
if (type != CHAR_NAME && type != CHAR_VALUE) {
break;
}
i++;
}
currentToken = StringUtils.fromCacheOrNew(sqlCommand.substring(start, i));
currentTokenType = getTokenType(currentToken);
parseIndex = i;
return;
case CHAR_QUOTED: {
String result = null;
//内部的for循环用于找出第一对双引号中包含的字符
//如果双引号中包含的字符又有双引号,while循环继续寻找后面的字符
//比如对于"aaa""bbb",i先从第一个a开始,到达第二个"号时,if (chars[i] == '\"')为true,
//因为此时result为null,所以result = sqlCommand.substring(begin, i) = aaa
//接着退出for循环,因为chars[++i]=",所以while循环继续,此时begin从第一个b开始,
//进入到if (chars[i] == '\"')时,因为前面result = aaa,
//所以result += sqlCommand.substring(begin - 1, i) = aaa"bbb
//也就是说双引号中包含的字符如果是连续的两个""那么就表示"号自身
while (true) {
for (int begin = i;; i++) {
if (chars[i] == '\"') {
if (result == null) {
result = sqlCommand.substring(begin, i);
} else {
result += sqlCommand.substring(begin - 1, i); //begin - 1表示把前面的"号也加进来
}
break;
}
}
if (chars[++i] != '\"') { //比如"aaa""bbb"的场景,最终会转换成aaa"bbb
break;
}
i++;
}
currentToken = StringUtils.fromCacheOrNew(result);
parseIndex = i;
currentTokenQuoted = true;
currentTokenType = IDENTIFIER;
return;
}
case CHAR_SPECIAL_2:
//两个CHAR_SPECIAL_2类型的字符要合并。例如!=
if (types[i] == CHAR_SPECIAL_2) {
i++;
}
currentToken = sqlCommand.substring(start, i);
currentTokenType = getSpecialType(currentToken);
parseIndex = i;
return;
case CHAR_SPECIAL_1:
currentToken = sqlCommand.substring(start, i);
currentTokenType = getSpecialType(currentToken);
parseIndex = i;
return;
case CHAR_VALUE:
if (c == '0' && chars[i] == 'X') { //在initialize中已把x转换成大写X
// hex number
long number = 0;
start += 2;
i++;
while (true) {
c = chars[i];
if ((c < '0' || c > '9') && (c < 'A' || c > 'F')) {
checkLiterals(false);
currentValue = ValueInt.get((int) number);
currentTokenType = VALUE;
currentToken = "0";
parseIndex = i;
return;
}
number = (number << 4) + c - (c >= 'A' ? ('A' - 0xa) : ('0'));
if (number > Integer.MAX_VALUE) {
readHexDecimal(start, i);
return;
}
i++;
}
}
long number = c - '0';
while (true) {
c = chars[i];
if (c < '0' || c > '9') {
if (c == '.') {
readDecimal(start, i);
break;
}
if (c == 'E') {
readDecimal(start, i);
break;
}
checkLiterals(false);
currentValue = ValueInt.get((int) number);
currentTokenType = VALUE;
currentToken = "0";
parseIndex = i;
break;
}
number = number * 10 + (c - '0');
if (number > Integer.MAX_VALUE) {
readDecimal(start, i);
break;
}
i++;
}
return;
case CHAR_DOT:
if (types[i] != CHAR_VALUE) {
currentTokenType = KEYWORD;
currentToken = ".";
parseIndex = i;
return;
}
readDecimal(i - 1, i);
return;
case CHAR_STRING: {
String result = null;
//与CHAR_QUOTED类似
while (true) {
for (int begin = i;; i++) {
if (chars[i] == '\'') {
if (result == null) {
result = sqlCommand.substring(begin, i);
} else {
result += sqlCommand.substring(begin - 1, i);
}
break;
}
}
if (chars[++i] != '\'') {
break;
}
i++;
}
currentToken = "'";
checkLiterals(true);
currentValue = ValueString.get(StringUtils.fromCacheOrNew(result));
parseIndex = i;
currentTokenType = VALUE;
return;
}
case CHAR_DOLLAR_QUOTED_STRING: {
String result = null;
int begin = i - 1;
while (types[i] == CHAR_DOLLAR_QUOTED_STRING) {
i++;
}
result = sqlCommand.substring(begin, i);
currentToken = "'";
checkLiterals(true);
currentValue = ValueString.get(StringUtils.fromCacheOrNew(result));
parseIndex = i;
currentTokenType = VALUE;
return;
}
case CHAR_END:
currentToken = "";
currentTokenType = END;
parseIndex = i;
return;
default:
throw getSyntaxError();
}
}